/*                                                                      
 Copyright 2018 hzheng@gzhu.edu.cn              
                                                                         
 Licensed under the Apache License, Version 2.0 (the "License");         
 you may not use this file except in compliance with the License.        
 You may obtain a copy of the License at                                 
                                                                         
     http://www.apache.org/licenses/LICENSE-2.0                          
                                                                         
  Unless required by applicable law or agreed to in writing, software    
 distributed under the License is distributed on an "AS IS" BASIS,       
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and     
 limitations under the License.                                          
 */                                                                      
/*+***********************************************************
Filename: zh_exec_alu_v02.v
Description: a simple alu implementation.
             add/subtract only.
Modification:
  2018.07.28 createion by Zheng Hui
       08.06   add more instructuin support
       08.12   change i_alu_signed_flag to i_alu_unsigned_flag
               change adder to 33 bit width
               add signed/unsigned support

************************************************************-*/

module zh_exec_alu_v02(
  input[31:0] i_alu_data_a,
  input[31:0] i_alu_data_b,
  input i_alu_unsigned_flag,  //1=unsigned
  input i_alu_add_flag,     
  input i_alu_sub_flag,   
  input i_alu_and_flag,   
  input i_alu_or_flag,   
  input i_alu_xor_flag,
  input i_alu_sll_flag,   
  input i_alu_srl_flag,   
  input i_alu_sra_flag,
  input i_alu_compare_flag,
  input i_alu_enable,
  output [31:0] alu_result,
  output equal_flag,   //a=b
  output less_flag, //a<b
  output greater_equal_flag //a>=b
);

  wire adder_addsub = i_alu_enable & (i_alu_add_flag | i_alu_sub_flag); 
  
  wire adder_cin;
  wire [32:0] adder_in1_signed;
  wire [32:0] adder_in2_signed;
  wire [32:0] adder_in1;
  wire [32:0] adder_in2;
  wire [32:0] alu_sum_result_0;

  assign adder_in1_signed = {{(~i_alu_unsigned_flag) & i_alu_data_a[31]},i_alu_data_a};
  assign adder_in2_signed = {{(~i_alu_unsigned_flag) & i_alu_data_b[31]},i_alu_data_b};
  assign adder_in1 = {33{adder_addsub}} & (adder_in1_signed);
  assign adder_in2 = {33{adder_addsub}} & (i_alu_sub_flag ? (~adder_in2_signed) : adder_in2_signed);
  assign adder_cin = adder_addsub & i_alu_sub_flag;

  assign alu_sum_result_0 = {33{adder_addsub}} & (adder_in1 + adder_in2 + adder_cin);
  wire [31:0] alu_sum_result;
  assign alu_sum_result[31:0] = alu_sum_result_0[31:0];

  //shift
  wire adder_shift =  i_alu_enable & (i_alu_sll_flag | i_alu_srl_flag | i_alu_sra_flag);
  wire [4:0] shift_value = {5{adder_shift}} & i_alu_data_b[4:0];
  wire [31:0] sll_result = i_alu_data_a << shift_value;
  wire [31:0] srl_result = i_alu_data_a >> shift_value;
  wire [31:0] sra_mask=(32'b1) >> shift_value;
  wire [31:0] sra_result = (srl_result&sra_mask) | ({32{i_alu_data_a[31]}} & (~sra_mask));
  wire [31:0] alu_shift_result = ({32{i_alu_sll_flag}} & sll_result) 
                         | ({32{i_alu_srl_flag}} & srl_result) 
                         | ({32{i_alu_sra_flag}} & sra_result); 

  //logical operation
  wire [31:0] or_result = i_alu_data_a | i_alu_data_b;
  wire [31:0] and_result = i_alu_data_a & i_alu_data_b;
  wire [31:0] xor_result = i_alu_data_a ^ i_alu_data_b;

  wire [31:0] alu_logical_result = ({32{i_alu_or_flag}} & or_result) 
                         | ({32{i_alu_and_flag}} & and_result) 
                         | ({32{i_alu_xor_flag}} & xor_result); 

  //result
  assign alu_result = alu_sum_result | alu_shift_result | alu_logical_result;

  //flags
  assign equal_flag = ~(|xor_result);
  assign less_flag = alu_sum_result[31];
  assign greater_equal_flag = (~less_flag);

endmodule
